package bll

import (
	"context"
	"fmt"
	"github.com/panjf2000/ants/v2"
	log "ota/collector/logger"
	"ota/config"
	"ota/event"
	"ota/model"
	"ota/utils/cron"
	"sync"
	"time"
)

type pendingTimer struct {
	antPools *ants.PoolWithFunc
	locker   sync.Mutex
	*cron.Cron
	planId2EntryId map[int64]cron.EntryID
}

var (
	upPendingTimer = &pendingTimer{
		locker:         sync.Mutex{},
		Cron:           cron.New(cron.WithLocation(time.Local), cron.WithSeconds()),
		planId2EntryId: make(map[int64]cron.EntryID),
	}
)

func init() {
	Register(upPendingTimer)
}

func (a *pendingTimer) init() func() {

	var (
		timeout = config.Conf.OTA.Timeout
	)

	/*
	   	1. 超时最小值是300,5分钟
	     2. -1 表示不设置超时
	*/
	if timeout > 0 && timeout < 300 {
		timeout = 300
	}

	if aPool, err := ants.NewPoolWithFunc(500, func(i interface{}) {
		data := i.(*model.OtaUpgradeRequest)
		if err := Ota.Upgrade(context.Background(), data); err == nil {
			userUpgradeTimeoutTimer.onEvent(&event.UserUpgradeEventData{
				Id:              data.ID,
				PlanId:          data.PlanId,
				StartTime:       time.Now().Unix(),
				TimeoutDuration: int64(timeout),
			})
		}
	}, poolOption...); err != nil {
		panic(fmt.Errorf("create ants pool fail :%+v", err))
	} else {
		a.antPools = aPool
	}

	a.Start()
	return func() {}
}

func (a *pendingTimer) onEvent(evtData *event.Data) {

	var (
		startTime = time.Unix(0, 0)
	)
	switch evtData.Data.(type) {
	case *model.UpgradePlanCreateRequest:
		startTime = time.Unix(evtData.Data.(*model.UpgradePlanCreateRequest).StartTime, 0)
		break
	case *model.UpgradePlanUpdateRequest:
		startTime = time.Unix(*evtData.Data.(*model.UpgradePlanUpdateRequest).StartTime, 0)
		break
	}

	spec := fmt.Sprintf("%v %v %v %v %v *", startTime.Second(), startTime.Minute(), startTime.Hour(), startTime.Day(), int(startTime.Month()))

	var (
		callBackMap = map[model.OtaUpgradePlanOpType]func(string, *event.Data) error{
			model.UpgradePlanOpCreate:   a.eventCreateUpgradePlan,
			model.UpgradePlanOpUpdate:   a.eventUpdateUpgradePlan,
			model.UpgradePlanOpDelete:   a.eventDeleteUpgradePlan,
			model.UpgradePlanOpCanceled: a.eventDeleteUpgradePlan,
		}
	)

	if _, ok := callBackMap[evtData.Type]; !ok {
		log.Errorf("Unknown event type %d", evtData.Type)
		return
	}

	log.Infof("Ota onEvent(%v,spec:%v start_time:%v %v) error:%v", evtData.Type.String(), spec, startTime, startTime.String(), callBackMap[evtData.Type](spec, evtData))
}

func (a *pendingTimer) eventCreateUpgradePlan(spec string, evtData *event.Data) error {

	a.locker.Lock()
	defer a.locker.Unlock()

	var (
		err     error
		entryId cron.EntryID
		data    = evtData.Data.(*model.UpgradePlanCreateRequest)
	)

	if _, ok := a.planId2EntryId[data.Id]; !ok {
		entryId, err = upPendingTimer.AddFunc(spec, a.timerCallBack, data.Id)
		if err != nil {
			return err
		}
		a.planId2EntryId[data.Id] = entryId
	}

	return err
}

func (a *pendingTimer) eventUpdateUpgradePlan(spec string, evtData *event.Data) error {

	a.locker.Lock()
	defer a.locker.Unlock()
	var (
		err  error
		data = evtData.Data.(*model.UpgradePlanUpdateRequest)
	)

	if entryId, ok := a.planId2EntryId[*data.Id]; ok {
		upPendingTimer.Remove(entryId)
		entryId, err = upPendingTimer.AddFunc(spec, a.timerCallBack, *data.Id)
		if err != nil {
			return err
		}
	}

	return err
}

func (a *pendingTimer) eventDeleteUpgradePlan(spec string, evtData *event.Data) error {

	a.locker.Lock()
	defer a.locker.Unlock()
	var (
		err  error
		data = evtData.Data.(*model.UpgradePlanDeleteRequest)
	)

	if entryId, ok := a.planId2EntryId[data.Id]; ok {
		upPendingTimer.Remove(entryId)
		delete(a.planId2EntryId, data.Id)
	}

	return err
}

func (a *pendingTimer) submitUpgradeTask(dataS []*model.OtaUpgradeRequest) error {

	for _, data := range dataS {
		if err := a.antPools.Invoke(data); err != nil {
			return err
		}
	}
	return nil
}

func (a *pendingTimer) timerCallBack(args ...interface{}) {

	var (
		planId = args[1].(int64)
	)

	defer func() {
		a.onEvent(&event.Data{
			Type: model.UpgradePlanOpDelete,
			Data: &model.UpgradePlanDeleteRequest{Id: planId},
		})
	}()

	err := UpgradePlan.execOtaSilenceUpgrade(context.Background(), planId, a.submitUpgradeTask)
	if err != nil {
		log.Errorf("Ota OnEvent error:%v", err)
		return
	}
}
